#include "android.h"
#include <Windows.h>

#define BUFFER_SIZE 1000
#define MAX_PIPE_BUFFER_SIZE 65535

int readPipe(HANDLE hPipe_out, TCHAR *out_buffer, int out_max){
	DWORD byteRead = 0;
	DWORD availByte = 0;
	TCHAR temp;
	memset(out_buffer, 0, sizeof(TCHAR)*out_max); //reset the buffer
	PeekNamedPipe(hPipe_out, &temp, sizeof(TCHAR), NULL, &availByte, NULL);
	if (availByte == 0) //nothing to be read
		return byteRead;
	//read what is available
	ReadFile( hPipe_out, out_buffer, out_max-1 * sizeof(TCHAR), &byteRead, NULL);
	return byteRead;
}

void parseDevice(TCHAR* input, vector<TCHAR*> *output){
	//a device name is it's serial number, it is either delimited by a space or tab	
	TCHAR* token = wcstok(input, L"\n");
	while(token != NULL){
		int i, maxLen = wcslen(token);
		for (i = 0;token[i] != L' ' && token[i] != L'\0' && i < maxLen; i++);
		TCHAR* deviceName = new TCHAR[i+1];
		memset(deviceName, 0, sizeof(TCHAR)*(i+1));
		memcpy(deviceName, token, sizeof(TCHAR)*i);
		output->push_back(deviceName);
		token = wcstok(NULL, L"\n");
	}
}

void parseList(TCHAR* input, vector<TCHAR*> *output){
	TCHAR* token = wcstok(input, L"\n");
	while(token != NULL){
		output->push_back(token);
		token = wcstok(NULL, L"\n");
	}
}

AndroidDevice::AndroidDevice(TCHAR* device_name){
	defaultMP3Folder = L"MP3";
	defaultPlayListFolder = L"Playlist";
	defaultSyncFolder = L"FoobarSync";
	syncFolder = playlistFolder = mp3Folder = false;
	device_enum_name = device_name;
	checkFolders();
}

bool AndroidDevice::hasFile(TCHAR* filename){
	//compare with file list
	//mp3 extension compare only with mp3 list
	//m3u extension compare only with m3u list
	return false;
}

void AndroidDevice::syncFile(TCHAR* filename){
	//run adb -s <devicename> push <file> <trg> after running hasFile to check file existance
}

void AndroidDevice::removeFile(TCHAR* filename){
	int len = wcslen(filename) + wcslen(device_enum_name) + 12;
	TCHAR* params = new TCHAR[len+1];
	swprintf(params, L"-s %s shell rm %s", device_enum_name, filename);
	Android::execute(L"adb", params, NULL);
}

void AndroidDevice::removeFolder(TCHAR* foldername){
	int len = wcslen(foldername) + wcslen(device_enum_name) + 15;
	TCHAR* params = new TCHAR[len+1];
	swprintf(params, L"-s %s shell rmdir %s", device_enum_name, foldername);
	Android::execute(L"adb", params, NULL);
}

void AndroidDevice::renameFile(TCHAR* o_filename, TCHAR* n_filename){
	int len = wcslen(o_filename) + wcslen(n_filename) + wcslen(device_enum_name) + 13;
	TCHAR* params = new TCHAR[len+1];
	swprintf(params, L"-s %s shell mv %s %s", device_enum_name, o_filename, n_filename);
	Android::execute(L"adb", params, NULL);
}

void AndroidDevice::checkFolders(){
	vector<TCHAR*> fileList;
	Android::CALLBACK_STRUCT callback;
	callback.storage = &fileList;
	callback.pfunc = parseList;

	//checking sync folder
	int syncBasePathlen = wcslen(device_enum_name) + 23;
	TCHAR* params = new TCHAR[syncBasePathlen];
	memset(params, 0, sizeof(TCHAR)*syncBasePathlen);
	swprintf(params, L"-s %s shell ls /sdcard/", device_enum_name);
	Android::execute(L"adb", params, &callback);
	free(params);

	for (vector<TCHAR*>::iterator itr = fileList.begin(); itr != fileList.end(); ++itr){
		if (wcscmp(*itr, defaultSyncFolder) == 0){
			syncFolder == true;
			break;
		}
	}	

	if (syncFolder){
		fileList.clear();
		syncBasePathlen += wcslen(defaultSyncFolder);
		params = new TCHAR[syncBasePathlen];
		memset(params, 0, sizeof(TCHAR)*syncBasePathlen);
		swprintf(params, L"-s %s shell ls /sdcard/%s/", device_enum_name, defaultSyncFolder);
		Android::execute(L"adb", params, &callback);
		free(params);

		for (vector<TCHAR*>::iterator itr = fileList.begin(); (!mp3Folder || !playlistFolder) && itr != fileList.end(); ++itr){
			if (wcscmp(*itr, defaultMP3Folder) == 0){
				mp3Folder == true;
			}else if (wcscmp(*itr, defaultPlayListFolder) == 0){
				playlistFolder = true;	
			}
		}
	}
}

void AndroidDevice::getFileList(TCHAR* foldername){
	int syncBasePathlen = wcslen(device_enum_name) + wcslen(defaultSyncFolder) + 23;
	int mp3Pathlen = syncBasePathlen + wcslen(defaultMP3Folder) + 1;
	Android::CALLBACK_STRUCT callback;
	callback.storage = &vecMp3;
	callback.pfunc = parseList;
	TCHAR* params = new TCHAR[mp3Pathlen];
	memset(params, 0, sizeof(TCHAR)*mp3Pathlen);
	swprintf(params, L"-s %s shell ls /sdcard/%s/%s/", device_enum_name, defaultSyncFolder, defaultMP3Folder);
	Android::execute(L"adb", params, &callback);
	free(params);

	int playlistPathlen = syncBasePathlen + wcslen(defaultPlayListFolder) + 1;
	callback.storage = &vecPlaylist;
	params = new TCHAR[mp3Pathlen];
	memset(params, 0, sizeof(TCHAR)*playlistPathlen);
	swprintf(params, L"-s %s shell ls /sdcard/%s/%s/", device_enum_name, defaultSyncFolder, defaultPlayListFolder);
	Android::execute(L"adb", params, &callback);
	free(params);
}

vector<TCHAR*>* Android::getDeviceList(){
	//run "adb devices"
	vector<TCHAR*> *deviceList = new vector<TCHAR*>;
	Android::CALLBACK_STRUCT callback;
	callback.storage = deviceList;
	callback.pfunc = parseDevice;

	Android::execute(L"adb", L"devices", &callback);
	return deviceList;
}

bool Android::execute(TCHAR* proc_name, TCHAR* cmdline, CALLBACK_STRUCT *callback){
	PROCESS_INFORMATION piProcInfo; 
	STARTUPINFO siStartInfo;
	SECURITY_ATTRIBUTES saSecAttr;
	HANDLE hReadPipe, hWritePipe;	
	bool bSuccess = false;

	memset(&siStartInfo, 0, sizeof(STARTUPINFO));
	memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
	memset(&saSecAttr, 0, sizeof(SECURITY_ATTRIBUTES));

	saSecAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saSecAttr.bInheritHandle = TRUE;
    CreatePipe( &hReadPipe, &hWritePipe, &saSecAttr, MAX_PIPE_BUFFER_SIZE);

	siStartInfo.cb = sizeof(STARTUPINFO);
	siStartInfo.dwFlags = STARTF_USESTDHANDLES;
	siStartInfo.wShowWindow = SW_HIDE;
	siStartInfo.hStdOutput = hWritePipe;
	siStartInfo.hStdError = hWritePipe;
	siStartInfo.hStdInput = NULL;
	int len = wcslen(proc_name) + wcslen(cmdline) + 2;
	TCHAR* cmd = new TCHAR[len];
	swprintf(cmd, L"%s %s", proc_name, cmdline);	

	if(CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &piProcInfo)){
		//process stdout until the process has finished
		DWORD retCode = WAIT_TIMEOUT;
		TCHAR* buffer = new TCHAR[BUFFER_SIZE];
		memset(&buffer, 0, BUFFER_SIZE*sizeof(TCHAR));

		TCHAR* input = new TCHAR[0];
		while (retCode != WAIT_OBJECT_0){
			retCode = WaitForSingleObject(piProcInfo.hProcess, 100);
			if (retCode == WAIT_ABANDONED) break; //crashed
			while (readPipe(hReadPipe, buffer, BUFFER_SIZE) > 0){
				input = wcscat(input, buffer);
				memset(&buffer, 0, BUFFER_SIZE*sizeof(TCHAR));
			}
		}
		CloseHandle (hWritePipe);
		CloseHandle (hReadPipe);
		CloseHandle (piProcInfo.hProcess);
		CloseHandle (piProcInfo.hThread);
		free(cmd);		
		//parse input
		if (callback != NULL)
			callback->pfunc(input, callback->storage);
		return (retCode != WAIT_ABANDONED);
	} else {
		free(cmd);
		return false;
	}
}